package tv.dyndns.kishibe.server;

import java.io.IOException;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.openid4java.association.AssociationException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.consumer.VerificationResult;
import org.openid4java.discovery.DiscoveryException;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.Identifier;
import org.openid4java.message.MessageException;
import org.openid4java.message.ParameterList;

import tv.dyndns.kishibe.client.packet.PacketUserData;
import tv.dyndns.kishibe.server.database.Database;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.inject.Inject;

/**
 * OpenID からの戻り値を処理するための {@link Servlet}.
 */
@SuppressWarnings("serial")
public class OpenIdResponseServlet extends HttpServlet {
	private static final Logger logger = Logger.getLogger(OpenIdResponseServlet.class.toString());
	private static final String COOKIE_NAME_USER_CODE = "user_code";
	private static final String REDIRECT_URL = "http://kishibe.dyndns.tv:8080/QMAClone/";
	private final ConsumerManager consumerManager;
	private final Database database;

	@Inject
	public OpenIdResponseServlet(ConsumerManager consumerManager, Database database) {
		this.consumerManager = Preconditions.checkNotNull(consumerManager);
		this.database = Preconditions.checkNotNull(database);
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		int userCode = getUserCode(request);
		if (userCode == 0) {
			String message = "ユーザーコードが識別できませんでした";
			responseRequest(response, message);
			logger.log(Level.WARNING, message);
			return;
		}

		// extract the parameters from the authentication response
		// (which comes in as a HTTP request from the OpenID provider)
		ParameterList parameterList = new ParameterList(request.getParameterMap());

		// retrieve the previously stored discovery information
		DiscoveryInformation discovered = (DiscoveryInformation) request.getSession().getAttribute(
				"openid-disc");

		// extract the receiving URL from the HTTP request
		StringBuffer receivingURL = request.getRequestURL();
		String queryString = request.getQueryString();
		if (queryString != null && queryString.length() > 0)
			receivingURL.append("?").append(request.getQueryString());

		// verify the response; ConsumerManager needs to be the same
		// (static) instance used to place the authentication request
		VerificationResult verification;
		try {
			verification = consumerManager.verify(receivingURL.toString(), parameterList,
					discovered);
		} catch (MessageException | DiscoveryException | AssociationException e) {
			String message = "OpenIDによる認証に失敗しました";
			logger.log(Level.WARNING, message, e);
			responseRequest(response, message);
			return;
		}

		// examine the verification result and extract the verified identifier
		Identifier verified = verification.getVerifiedId();
		if (verified == null) {
			String message = "OpenIDによる認証に失敗しました";
			logger.log(Level.WARNING, message);
			responseRequest(response, message);
			return;
		}

		String identifier = verified.getIdentifier();

		PacketUserData userData = null;

		try {
			userData = database.getUserDataByIdentyfyString(identifier);
		} catch (SQLException e) {
			String message = "データベースへのアクセスに失敗しました\n" + Throwables.getStackTraceAsString(e);
			responseRequest(response, message);
			return;
		}

		if (userData == null) {
			String message = "ユーザーデータが見つかりませんでした";
			responseRequest(response, message);
			return;
		}
		
		
	}

	/**
	 * {@link HttpServletRequest} に含まれるユーザーコードを返す
	 * 
	 * @param request
	 *            {@link HttpServletRequest}
	 * @return ユーザーコード。存在しない場合は0。
	 */
	private int getUserCode(HttpServletRequest request) {
		Cookie[] cookies = request.getCookies();
		if (cookies == null) {
			return 0;
		}
		for (Cookie cookie : cookies) {
			if (!cookie.getName().equals(COOKIE_NAME_USER_CODE)) {
				continue;
			}

			return Integer.parseInt(cookie.getValue());
		}

		return 0;
	}

	private void responseRequest(HttpServletResponse response, String message) throws IOException {
		response.sendRedirect(REDIRECT_URL);
		response.setContentType("text/plain");
		response.setCharacterEncoding("utf-8");

		try (ServletOutputStream outputStream = response.getOutputStream()) {
			outputStream.write(message.getBytes("utf-8"));
		}
	}
}
